/*
 * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#if PRJCONF_NET_EN
#include "cmd_ftp.h"
#include "cmd_util.h"
#include "fs/fatfs/ff.h"
#include "common/framework/net_ctrl.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "kernel/os/os.h"
#include "net/wlan/wlan.h"
#include "lwip/sockets.h"
#include "lwip/netif.h"
#include "lwip/sockets.h"
#include "lwip/netif.h"
#include "lwip/netdb.h"

#if CMD_DESCRIBE
static const char *ftp_help_info =
	"[*] -c :  client mode , upload file to ftp server\n"
    "[*] -f : file name\n"
    ;
#endif /* CMD_DESCRIBE */

#define BUFFER_SIZE 1024
// ftp上传的端口 
#define SERVER_PORT 21  

// ftp服务器的ip地址
static char ip_addr[30] = {0} ;
// 待上传的文件名
static char file_name[100] = {0};
// ftp上传线程
static OS_Thread_t  os_thread_t_ftp = {OS_INVALID_HANDLE};

static enum cmd_status cmd_ftp_help_exec(char *cmd)
{
#if CMD_DESCRIBE
	CMD_LOG(1, "%s\n", ftp_help_info);
#endif
	return CMD_STATUS_ACKED;
}

// 函数用于解析FTP命令中的参数值
static bool parse_ftp_command(char *command) {
    char *token = NULL;
    char *delim = " ";
    
    cmd_memset(ip_addr , 0 , sizeof(ip_addr));
    cmd_memset(file_name , 0 , sizeof(file_name));

    // 使用空格作为分隔符来切分命令字符串
    token = strtok(command, delim);
    
    while (token != NULL) {
        // 检查参数是否以'-'开头
        if (token[0] == '-') {
            // 如果是'-c'参数，则打印其后的IP地址
            if (cmd_strcmp(token, "-c") == 0) {
                token = strtok(NULL, delim); // 获取下一个参数
                if (cmd_strlen(token) >0)
                {
                        cmd_strlcpy(ip_addr , token , sizeof(ip_addr));
                       // CMD_DBG("ip_addr = [%s] , token = [%s]\n", ip_addr , token );
                }else{
                        CMD_ERR("ip adrr is empty");
                }
            }
            // 如果是'-file'参数，则打印其后的文件名
            else if (strcmp(token, "-f") == 0) {
                token = strtok(NULL, delim); // 获取下一个参数
                if (cmd_strlen(token) >0)
                {
                        cmd_strlcpy(file_name , token , sizeof(file_name));
                        //CMD_DBG("file_name = [%s] , token = [%s]\n", file_name , token );
                }else{
                        CMD_ERR("file name is empty");
                }
            }
            // 添加其他参数的解析逻辑（如果有的话）
        }
        token = strtok(NULL, delim); // 继续获取下一个参数
    }
    CMD_DBG("ip_addr = [%s] , file name = [%s]\n", ip_addr , file_name );
    return cmd_strlen(ip_addr) && cmd_strlen(file_name);
}

static int get_domain_ip(const char *domain, unsigned int *ip)
{
	if(domain == NULL || ip == NULL){
		return -1;
	}

	struct hostent *host = gethostbyname(domain);
	if(host == NULL){
		return -1;
	}

	if(host->h_addrtype != AF_INET){
		return -1;
	}

	int ipint = 0;
	memcpy(&ipint, *host->h_addr_list,sizeof(unsigned int));
	*ip = ipint;
	return 0;
}

static  int upload_function() {
    int sockfd;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    // 创建套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        CMD_ERR("ERROR opening socket\n");

    // 初始化服务器地址结构
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVER_PORT);

    #if 1
        unsigned int ip;
        int ret =get_domain_ip(ip_addr,&ip);
        if (ret < 0 )
        {
             CMD_ERR("ERROR get_domain_ip \n");
        }
        serv_addr.sin_addr.s_addr = ip;
    #else 
    if (inet_pton(AF_INET, ip_addr, &serv_addr.sin_addr) <= 0)
        CMD_ERR("ERROR converting IP address\n");
    #endif 

    // 连接服务器
    if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
        CMD_ERR("ERROR connecting\n");

    // 读取欢迎信息
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)
        CMD_ERR("ERROR reading from socket\n");
    CMD_DBG("read welcome: %s\n", buffer);

    // 发送用户名
    char username[] = "USER admin\r\n";
    send(sockfd, username, strlen(username), 0);
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)
        CMD_ERR("ERROR reading from socket\n");
    CMD_DBG("read USER: %s\n", buffer);

    // 发送密码
    char password[] = "PASS admin\r\n";
    send(sockfd, password, strlen(password), 0);
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)
        CMD_ERR("ERROR reading from socket\n");
    CMD_DBG("read PASS: %s\n", buffer);

    // 发送被动模式命令
    char pasv_command[] = "PASV\r\n";
    send(sockfd, pasv_command, strlen(pasv_command), 0);
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)
        CMD_ERR("ERROR reading from socket\n");
    CMD_DBG("read PASV:%s\n", buffer);

    // 解析PASV命令响应
    unsigned int ip1, ip2, ip3, ip4, port1, port2;
    sscanf(buffer, "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\n", &ip1, &ip2, &ip3, &ip4, &port1, &port2);
    char data_ip[20];
    sprintf(data_ip, "%u.%u.%u.%u", ip1, ip2, ip3, ip4);
    int data_port = port1 * 256 + port2;

    // 创建数据连接
    int data_sockfd;
    struct sockaddr_in data_serv_addr;
    if ((data_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        CMD_ERR("ERROR opening data socket\n");
    memset(&data_serv_addr, 0, sizeof(data_serv_addr));
    data_serv_addr.sin_family = AF_INET;
    data_serv_addr.sin_port = htons(data_port);
    #if 1
        ret =get_domain_ip(data_ip,&ip);
        if (ret < 0 )
        {
             CMD_ERR("ERROR get_domain_ip \n");
        }
        data_serv_addr.sin_addr.s_addr = ip ;
    #else
    if (inet_pton(AF_INET, data_ip, &data_serv_addr.sin_addr) <= 0)
        CMD_ERR("ERROR converting data IP address\n");
    #endif
    
    if (connect(data_sockfd, (struct sockaddr*)&data_serv_addr, sizeof(data_serv_addr)) < 0)
        CMD_ERR("ERROR connecting data socket\n");

    // 发送STOR命令
    char stor_command_style[] = "STOR %s\r\n";
    char stor_command[100] = {0};
    // 获取文件的属性
    FILINFO fileInfo ;
	f_stat(file_name,&fileInfo);
    sprintf(stor_command , stor_command_style , fileInfo.fname);
    send(sockfd, stor_command, strlen(stor_command), 0);
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)
        CMD_ERR("ERROR reading from socket\n");
    CMD_DBG("read STOR : %s\n", buffer);

    // 打开本地文件
    FIL file ; 
    FRESULT fr_result  = f_open(&file , file_name, FA_READ);
    if (fr_result != FR_OK)
        CMD_ERR("ERROR opening local file, file_name = [%s]\n",file_name);

    // 上传文件内容
    UINT bytes_read;
    CMD_DBG("start read data from file\n");
    while ( true ) {
        fr_result = f_read(&file , buffer , sizeof(buffer), &bytes_read); 
        if (fr_result != FR_OK)
        {
                CMD_ERR("ERROR read data from file\n");
        }else{
            if (bytes_read > 0 )
            {
                if (send(data_sockfd, buffer, bytes_read, 0) < 0){
                    CMD_ERR("ERROR writing to data socket\n");
                    break; 
                }else{
                       // CMD_DBG("send data success , len = %d\n", bytes_read);
                }
            }else{
                break; 
            }
        }
    }
    CMD_DBG("read data from file end\n");
    // 关闭数据连接
    close(data_sockfd);

    // 关闭文件
    f_close( &file );

    // 读取上传结果
    if (recv(sockfd, buffer, sizeof(buffer), 0) < 0){
        CMD_ERR("ERROR reading from socket\n");
    }else{
        CMD_DBG("ftp upload file end \n");
    }
    // 关闭控制连接
    close(sockfd);
    return 0;
}

static void ftp_upload_task(void *param){
    upload_function();
    OS_ThreadDelete(&os_thread_t_ftp);
}

enum cmd_status cmd_ftp_exec(char *cmd)
{
	if (cmd_strcmp(cmd, "help") == 0 || cmd_strcmp(cmd, "-h") == 0) {
		cmd_ftp_help_exec(cmd);
		return CMD_STATUS_ACKED;
	}
    
    // 参数解析
    bool ret = parse_ftp_command(cmd);
    if (!ret)
    {
        CMD_ERR("parse ftp command error\n");
		cmd_ftp_help_exec(cmd);
		return CMD_STATUS_FAIL;
    }
    
    // 检查网络是否正常 
    struct netif *nif = g_wlan_netif;
	if (nif == NULL || !NET_IS_IP4_VALID(nif)) {
		CMD_ERR("net is down, ftp upload start failed\n");
		return CMD_STATUS_FAIL;
	}

    // 检测文件是否存在
    FILINFO fileInfo ;
	FRESULT f_result = f_stat(file_name,&fileInfo);
	if (f_result != FR_OK)
	{
			CMD_ERR("[%s] not exist, please check file path\n", file_name);
			return CMD_STATUS_FAIL ;
	}

    // 检测上传线程是否存在
    if (OS_ThreadIsValid( & os_thread_t_ftp))
    {
        CMD_ERR("ftp already upload ,  please try again delay\n");
		return CMD_STATUS_FAIL;
    }

    // 创建上传线程
    if (OS_ThreadCreate(&os_thread_t_ftp,
		                    "ftp upload",
		                    ftp_upload_task,
		                    (void *)NULL,
		                    OS_THREAD_PRIO_APP,
		                    4 * 1024) != OS_OK) {
			CMD_ERR("create ftp upload task failed\n");
			return CMD_STATUS_FAIL;
	}else{
        CMD_DBG("start upload file[%s] to [%s] \n" , file_name , ip_addr);
    }
	return CMD_STATUS_OK;
}

#endif /* PRJCONF_NET_EN */
